const mysql = require('mysql2');
const md5 = require('md5');
let cache = require('./cache');

//sql语句缓存
let cache_sql = {};
//Pool数据库连接池，DB数据库操作，Table表结构数据，有表的字段，主键，以及表名
let Pool,DB,TABLE;
//初始化放在文件尾
DB={
    //查询主方法，内置多种方法
    table:table,
    //原生查询
    select:select,
    //启动模型
    start_model:async ()=>{DB['model'] = await require('./model')(DB,Library);},
    //创建实例
    create:this.init,
    //初始化,启用缓存
    start:async ()=>{
       let Table = await Library.getTable(DB);
       //初始化缓存表
       Object.keys(Table).forEach(e=>cache.setTable(e))
       return DB;
    },
    //声明生命周期
    life:life,
    //启动缓存功能
};

//声明生命周期以及方法对象集
function life(create){
    //生命周期
    let life_Cycle = {
        //一切的开始开始
        start: create.start,
        //查询前调用getSql后，中间
        medi: create.medi,
        //查询结束
        end: create.end,
    };
    return {table};
}

//================================每一次操作必然是要走table()，选择控制表,此方法每次查询只能使用一次且必须调用
function table(...tab){
    let fun = require('./sqlfun');
    const Fun = fun({
        //获取全部记录,全字段或自定义字段
         get,
         //内容
         value,
         //非
         noGet,
         //非first
         noFirst,
         //与判断
         where,
         //不参与提前编译，适用于多表判断
         onWhere,
         //或判断
         orWhere,
         //修改字段
         update,
         //自增
         increment,
         //自减
         decrement,
         //获取一条记录
         first,
         //排序
         orderBy,
         //获取多少条数据
         limit,
         //跳过的数据
         offset,
         //增加一条或多条数据
         insert,
         //增加数据后返回增加后的id
         insertId,
         //获取生成的sql语句
         toSql,
         //内连接
         join,
         //左连接
         join_left,
         //右连接
         join_right,
         //删除
         delete:deletes,
         //插入sql语句的字符串
         many,
         onn,
         oron,
         on,
         count,
         //分页
         paging,
     })


    //sql语句的顺序,Param是数据，防止sql注入的，也就是被？代替的部分
    let Sql_Array=[],Param=[],Table=tab;
    //生命周期
    let lifeCycle = typeof life_Cycle!='undefined'?life_Cycle:{start(){},medi(){},end(){}}

    lifeCycle.start && lifeCycle.start();

    return Fun.table;
//=================================================一级封装，基础操作，其他封装都依托一以下封装的方法
    // 判断条件，field为字段,jud为判断表达式，可以不填，默认为=，不填时jud=term，term=查询的数据，可以为数组
    function where(field,jud,term,many={left:"",right:""}){
        if(Judge.isInvalid(term)){term=jud; jud="="}else{
            if(typeof term == 'object' && !Array.isArray(term)){
                many.left = term.left?term.left:"";
                many.right = term.right?term.right:"";
                term=jud; jud="=";
            }
        }
        if(!Array.isArray(term)){term=[term]};
        // (Table.length>1 && !field.includes(".")) && (field="`"+Table[0]+"`."+field);
        let sql = "",end = "";
        term.forEach(element => {
            sql+=end+many.left+field+" "+jud+' ?'+many.right;end=" or ";
            Param.push(element);
        });
        Sql_Array.push({where:sql});
        return Fun.where;
    }

    //or判断
    function orWhere(field,jud,term,many={left:"",right:""}){
        if(Judge.isInvalid(term)){
            term=jud; jud="="
        }
        else{
            if(typeof term == 'object' && !Array.isArray(term)){
                many.left = term.left?term.left:"";
                many.right = term.right?term.right:"";
                term=jud; jud="=";
            }
        }
        Sql_Array.push({orWhere:many.left+field+jud+" ?"+many.right});
        Param.push(term);
        return Fun.orWhere;
    }

    //on 与 此方法不参与预编译
    function onn(field,jud,term){
        if(Judge.isInvalid(term)){term=jud; jud="="}
        if(!Array.isArray(term)){term=[term]}
        let sql = "",end = "";
        term.forEach(element => {
            sql+=end+field+jud+"? ";end="and ";
            Param.push(element);
        });
        Sql_Array.push({on:sql});
        return Fun.where;
    }

    //on 或方法 此方法不参与预编译
    function oron(field,jud,term){
        if(Judge.isInvalid(term)){term=jud; jud="="}
        if(!Array.isArray(term)){term=[term]}
        let sql = "",end = "";
        term.forEach(element => {
            sql+=end+field+jud+"? ";end=" or ";
            Param.push(element);
        });
        Sql_Array.push({orWhere:sql});
        return Fun.where;
    }

    //此方法参与预编译
    function on(field,jud,term){
        if(Judge.isInvalid(term)){term=jud; jud="="}
        if(!Array.isArray(term)){term=[term]}
        let sql = "",end = "";
        term.forEach(element => {
            sql+=end+field+jud+element;end=" and ";
        });
        Sql_Array.push({on:sql});
        return Fun.where;
    }
    

    //此方法参与预编译
    function onWhere(field,jud,term){
        if(Judge.isInvalid(term)){term=jud; jud="="}
        if(!Array.isArray(term)){term=[term]}
        let sql = "",end = "";
        term.forEach(element => {
            sql+=end+field+jud+element;end=" and ";
        });
        Sql_Array.push({where:sql});
        return Fun.where;
    }

    
    //多添加进sql语句的东西
    function many(data="("){
        Sql_Array.push({many:data});
        return this;
    }

    //排序
    function orderBy(Params,desc="") {
        // desc 是降序
        Sql_Array.push({orderBy:Params+" "+desc});
        return Fun.orderBy;
    }


    //分页 获取几条数据
    function limit(Params) {
        Sql_Array.push({limit:'limit '+Params});
        return Fun.limit;
    }

    //分页 跳过几条数据
    function offset(Params) {
        Sql_Array.push({offset:'offset '+Params});
        return Fun.offset;
    }

    //返回结果,返回输出结果
    async function get(field = "*"){
        let SQL = Sql.toSql({Sql_Array,Table},'select',field);
        //调用生命周期函数
        lifeCycle.medi && lifeCycle.medi();
        let data =  await select(SQL,Param);  
        //执行结束生命周期
        lifeCycle.end(data);
        return data;
    }

    async function value(data){
        let a = await first();
        return !Judge.isInvalid(a)?a[data]:undefined;
    }

    //给外面看的sql语句
    function toSql(){
        return 'select '+"* from "+Table+" "+ Sql.toCondition(Sql_Array,Table);
    }

    //获取一条数据
    async function first(params) {
        let data = await limit(1).get(params);
        //执行结束生命周期
        lifeCycle.end && lifeCycle.end(data);
        return data.length>0?data[0]:undefined;
    }

    //分页功能
    function paging(page=1,number=10){
        return limit((page-1)*number+","+number);
    }
    
    //修改操作
    async function update(data){
        let SQL = Sql.toSql({Sql_Array,Table},'update',data);
        return (await select(SQL,Param)).affectedRows;
    }

    //删除操作
    async function deletes(){
        let SQL = Sql.toSql({Sql_Array,Table},'delete');
        return (await select(SQL,Param)).affectedRows;
    }

    //添加数据
    async function insert(param){
        if(!Array.isArray(param)){param=[param]}
        //生成sql语句以及提取数据，返回是对象{sql:"",data:[]}
        let SQL = Sql.toSql({Sql_Array,Table},'insert',param);
        param.forEach(e=>{for(let a in e){Param.push(e[a])}})
        //执行sql语句返回成功插入的数据的数量
        return (await select(SQL,Param)).affectedRows;
    }

    async function insertId(param){
        if(!Array.isArray(param)){param=[param]}
        //生成sql语句以及提取数据，返回是对象{sql:"",data:[]}
        let SQL = Sql.toSql({Sql_Array,Table},'insert',param);
        param.forEach(e=>{for(let a in e){Param.push(e[a])}})
        //执行sql语句返回成功插入的数据的id
        return (await select(SQL,Param)).insertId;
    }

    //默认自减，第二个参数为需要减的数
    async function decrement(parameter,number=1) {
        let sql = 'update '+Table+' set '+parameter+"="+parameter+"-"+number+" "+Sql.toCondition(Sql_Array,Table);
        return (await select({sql,table:Table,type:'update'},Param)).affectedRows;
    }
    
    //默认自增，第二个参数为需要减的数
    async function increment(parameter,number=1) {
        let sql = 'update '+Table+' set '+parameter+"="+parameter+"+"+number+" "+Sql.toCondition(Sql_Array,Table);
        let data = await select({type:"update",sql,table:Table},Param);
        return data.affectedRows;
    }

    //count计算数据
    async function count(field=["*"]){
        let fie = await first(field.map(res=>`count(${res})`));
        fie = field.reduce((p,res)=>{
            p[res] = fie[`count(${res})`];
            return p;
        },{})
        if(field.length===1)return fie[field[0]];
        return fie;        
    }
//=======================二级封装，基于一级封装的基础上进行实现的功能
    async function noGet(data){
        data =Array.isArray(data)?data:[data];
        //获取表名并去除空格
        field = TABLE[Table.toString()].table_field;
        //数组合并
        field = !Judge.isInvalid(data)?field.concat(data):field;
        //去除重复项
        field = Judge.removeDuplicate(field);
        //调用get
        return await get(field);
    }


    async function noFirst(data){
        data =Array.isArray(data)?data:[data];
        //获取表名并去除空格
        field =  TABLE[Table.toString()].table_field;
        //数组合并
        field = !Judge.isInvalid(data)?field.concat(data):field;
        //去除重复项
        field = Judge.removeDuplicate(field);
        //调用get
        return await first(field);
    }


//==========================================================连表查询
    /*单纯的join是内连接，拥有二个参数，参数1右表，参数二表达式 */
    function join(table,bd){
        Sql_Array.push({join:{table:table,bd:bd}});
        return Fun.where;
    }

    /**左连接 */
    function join_left(table,bd){
        Sql_Array.push({join_left:{table:table,bd:bd}});
        return Fun.where;
    }

    /**右连接 */
    function join_right(table,bd){
        Sql_Array.push({join_right:{table:table,bd:bd}});
        return Fun.where;
    }


//=========================================查询()结尾
}

//==========================================原生sql操作，一切封装都是依托这个方法
async function select(SQL,Param){
    if(typeof SQL == 'string'){SQL = {sql:SQL,table:[],type:"gem"}}
    let {type,table,sql} = SQL;
    //查询缓存|清除缓存
    switch (type){
        case "select":{
            //获取缓存
            let data= cache.get(sql+Param,table);
            if(data){return data}
            break;
        }
        case "insert"://清除缓存
        case "update"://清除缓存
        case "delete":{ cache.delete(table);}
    }
    return new Promise((resolve,rej)=>{
        Pool.query(sql,Param,(err,res)=>{
            if(err){
                rej(err);
            }else{
                //添加缓存
                cache.set(table,sql+Param,res)
                //对其深拷贝
                resolve(JSON.parse(JSON.stringify(res)));
            }
            // Pool.end();
        });
    });
}

//================================辅助方法，用啦判断数据是否是无效数据
class Judge{
    //判断是否是无效变量，全部为空或未定义即为无效
    static isInvalid(data,bool=true){
        if(data===undefined){
            return true;
        }else if(Array.isArray(data)){
            //这是一个数组
            if(data.length===0){
                return true;
            }else if(bool){
                //bool为真则是严格模式，每一个数组元素都需要审查，但是只要有一个不为空即有效数据
                return Object.keys(data).every(e=>{this.isInvalid(data[e])})
            }else{
                //轻松模式，不为空就是有效数据
                return false;
            }            
        }else if((typeof data) == 'object'){
            //这是一个对象数据
            if(bool){
                //判断对象中的数据是否有效，全部都是无效才是无效，有一个有效就算有效
                return Object.keys(data).every(e=>{this.isInvalid(data[e])})
            }
            //走到这里一般都是基本数据了，字符串，数值等
        }else return data === '';
    }
    //数组去重removeDuplicate
    static removeDuplicate(anArrayOf){
        return [...new Set(anArrayOf)]
    } 
}



//================================获取数组中的指定数据,合成sql语句，核心类
class Sql{
    //生成中间的sql语句，不生成sql头语句
    static toSql(data,type,field){
        //查询sql语句是否存在
        let sql = cache_sql[md5(JSON.stringify([data,type,field]))];
        if(sql){return {type,table:data.Table,sql}}
        let fun = {
            insert:Sql.toInsert,
            select:Sql.toSelect,
            update:Sql.toUpdate,
            delete:Sql.toDelete,
        }
        //执行操作,执行对应的方法,sql为sql片段的数组，field为数据或字段部分，如查询就是字段，插入就是数据
        sql = fun[type](data,field);
        cache_sql[md5(JSON.stringify([data,type,field]))]=sql;
        return {type,table:data.Table,sql};
    }

    //生成插入sql语句
    static toInsert({Sql_Array,Table},field){
        let SQL = "insert into `"+Table.join("`")+"` (";
        //获取字段
        SQL += "`"+Object.keys(field[0]).join("`,`")+"`) values";
        let wh =  Object.keys(field[0]).map(a=>{return "?";});
        // 获取所有的value,以及问号
        wh = field.map(res =>"("+wh+")");
        //去除最后的逗号
        SQL += wh;
        return SQL;
    }
    //生成查询sql语句
    static toSelect({Sql_Array,Table},field){
        //重构get，添加因数，顺序
        let SQL='select '+field+' from `'+Table.join("`")+"` ";
        return SQL+=Sql.toCondition(Sql_Array,Table);
    }

    static toDelete({Sql_Array,Table},field){
        //删除语句
        let SQL = "delete `"+Table.join("`")+"` from `"+Table.join("`")+"` ";
        //生成条件查询语句
        SQL+=Sql.toCondition(Sql_Array,Table);
        return SQL;
    }

    //生成修改的sql语句
    static toUpdate({Sql_Array,Table},field){
        let SQL = "update `"+Table.join("`")+"` set ";
        //修改后的数据必须用''包裹不然报错，且id主键不可改
        SQL += Object.keys(field).reduce((par,value)=>{
            par.push(value+"='"+field[value]+"'");
            return par;
        },[])+" ";
        //生成条件查询语句
        SQL+=Sql.toCondition(Sql_Array,Table);
        return SQL;
    }
    static getUpdate(Params){
    }
    
    //获取条件语句，如where，orWhere等中部的语句，提供给select，update使用
    static toCondition(sql,Table){
        //整体和尾部
        let SQL = "",tail_sql="";
        sql.forEach(obj=>{
            Object.keys(obj).forEach(key=>{
                eval(key+"(obj[key])");
            });
        });
        return SQL;

        //===============================解析的方法
        function where(data){
            //判断是不是第一个where判断
            SQL += SQL.includes('where ') || tail_sql==="and"?("and "+data+" "):(SQL="where "+data+" ");
        }
        function orWhere(data){
            SQL += SQL.includes('where ') || tail_sql==="and"?("or "+ data+" "):("where " + data+" ");
        }
        function on(data){
            //判断是不是第一个where判断,并且前面是不是表名
            SQL += SQL.includes('where ') || tail_sql==="and"?("and " + data+" "):("on "+data+" ");
        }
        function orderBy(data){
            //判断一下前面有没有,
            SQL += SQL.includes("ORDER BY")?(","+data+" "):("ORDER BY "+data+" ");
        }
        function limit(data){
            SQL+= data+" ";
        }
        function offset(data){
            SQL+= data+" ";
        }
        function join(data){
            // bd是表达式
            //进行内连接
            SQL+='join '+data.table+' ';
            if(data.bd){
                SQL+='on '+data.bd+' ';tail_sql="and";
            }
        }
        function join_left(data){
            //进行左连接
            SQL+='left join '+data.table+' ';
            if(data.bd){
                SQL+='on '+data.bd+' ';tail_sql="and";
            }
        }
        function join_right(data){
            //进行右连接
            SQL+='right join '+data.table+' ';
            if(data.bd){
                SQL+='on '+data.bd+' ';tail_sql="and";
            }
        }
        function many(data){
            SQL+= data+" ";
        }
    }
}


//======================存放操作数据库配置，结构的方法
let Library={
    getTable:getTable,
}

//======================初始化，获取数据库的所有配置信息
async function getTable(DB){
    console.log('开始初始化');
    let a =await DB.select({type:"show",sql:'show tables',table:['']},[]);
    let table = {};
    for(res in a){
        for(key in a[res]){
            //key是表名，查询表中的key值
            let tab = await DB.select({type:"select",table:[''],sql:
                `SELECT
                    k.column_name,
                    k.table_name 
                FROM
                    information_schema.table_constraints t
                    JOIN information_schema.key_column_usage k USING ( constraint_name, table_schema, table_name ) 
                WHERE
                    t.constraint_type = 'PRIMARY KEY' 
                    AND k.table_name = '${a[res][key]}'`
            },[]);
            if(tab.length>=1){
                //获取表中的所有字段
                let field =await DB.select({type:"select",table:[],sql:`select COLUMN_NAME from information_schema.COLUMNS where table_name = '${a[res][key]}'`});
                field = field.map((res)=>{ return res.COLUMN_NAME;});
                table[tab[0].table_name] = {
                    table_name:tab[0].table_name,
                    table_key:tab[0].column_name,
                    table_field:field,
                }
            }
        }
    }
    console.log('初始化完成');
    return table;
}


//=====================================对外开放的入口
module.exports=DB;

//初始化方法
Pool =  main();
//连接数据库
function main(){
try{
    let config = require("./mysql.config.json");
        Pool = mysql.createPool({
                host     : config.ip,
                user     : config.user,
                password : config.pass,
                database : config.database,
                waitForConnections: true,
                connectionLimit: 10,
                queueLimit: 0
            });
    console.log("mysql连接成功");
    //开始初始化，获取全部表结构数据
    return Pool;
}catch(err){
    console.log("mysql连接/初始化失败"+err);
}
}